<?php

namespace App\Services\Passport;

use League\OAuth2\Client\Provider\GenericProvider;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;

class AccessToken
{
    private $accessToken = null;

    public function __construct($grant, $params, $options=[])
    {
        $this->load($grant, $params, $options);

        $this->boot($grant, $params);
    }

    protected function boot($grant, $params)
    {
        if ( !$this->accessToken ){
            $this->getAccessToken($grant, $params);

        }else{
            if ( $this->accessToken->hasExpired() ){
                if ( $this->accessToken->getRefreshToken() ){
                    $this->getAccessToken('refresh_token', $params, [
                        'refresh_token' => $this->accessToken->getRefreshToken()
                    ]);
                }else{
                    $this->getAccessToken($grant, $params);
                }
            }
        }
    }

    public function needRefreshToken()
    {
        $refresh_token = $this->accessToken->getRefreshToken();

        return $refresh_token && $this->accessToken->hasExpired();
    }

    public function getAccessToken($grant, $params, $options = [])
    {
        $provider = new GenericProvider($params);

        try {
            // Try to get an access token using the client credentials grant.
            $this->accessToken = $provider->getAccessToken($grant, $options);

            $this->save($grant, $params, $options);

        } catch (IdentityProviderException $e) {

            $data = $e->getResponseBody();

            if ( is_array($data) ){
                throw new \Exception($data['error'].':'.$data['message']);
            }else{
                throw new \Exception('AccessToken ERROR: '.$e->getMessage());
            }
        }
    }

    protected function load($grant, $params, $options)
    {
        $key = $this->cacheKey($grant, $params, $options);

        $this->accessToken = cache($key);
    }

    protected function save($grant, $params, $options)
    {
        $key = $this->cacheKey($grant, $params, $options);

        $expires = $this->accessToken->getExpires() - time();

        cache([$key => $this->accessToken], intval($expires/60));
    }

    protected function cacheKey($grant, $params, $options)
    {
        return md5($grant.serialize($params).serialize($options));
    }

    public function __toString()
    {
        return $this->accessToken->getToken();
    }

    public function __get($key)
    {
        $value = $this->accessToken->getValues();

        return $value[$key];
    }
}